package com.wxapp.pay.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wxapp.common.base.util.redis.RedisUtil;
import com.wxapp.common.constant.base.IBaseService;
import com.wxapp.common.constant.constant.CatchCommonConstant;
import com.wxapp.common.constant.enums.ClientTypeEnum;
import com.wxapp.common.constant.pojo.vm.ResultVm;
import com.wxapp.common.constant.pojo.vo.UserVo;
import com.wxapp.common.core.filter.except.ThrowMyException;
import com.wxapp.common.util.check.CheckUtil;
import com.wxapp.common.util.condition.ConditionUtil;
import com.wxapp.common.util.copy.CopyUtil;
import com.wxapp.pay.api.pojo.enums.listener.PayStatusEnum;
import com.wxapp.pay.api.pojo.feign.SaleFeign;
import com.wxapp.pay.api.pojo.pojo.entity.PayConfigDO;
import com.wxapp.pay.api.pojo.pojo.entity.PayLogsDO;
import com.wxapp.pay.api.pojo.pojo.entity.PayWayDO;
import com.wxapp.pay.api.pojo.pojo.po.vo.pay.QOrderInfoByCodeVo;
import com.wxapp.pay.api.pojo.pojo.vm.pay.PPayDetailVm;
import com.wxapp.pay.api.pojo.pojo.vm.pay.PPayPageVm;
import com.wxapp.pay.api.pojo.pojo.vm.pay.PPaySelectVm;
import com.wxapp.pay.api.pojo.pojo.vm.pay.PPayStatusVm;
import com.wxapp.pay.api.pojo.pojo.vm.payConfig.PConfigVm;
import com.wxapp.pay.api.pojo.pojo.vo.order.QOrderInfoVo;
import com.wxapp.pay.api.pojo.pojo.vo.pay.*;
import com.wxapp.pay.service.event.PayCallBackEvent;
import com.wxapp.pay.service.mapper.PayConfigMapper;
import com.wxapp.pay.service.mapper.PayLogsMapper;
import com.wxapp.pay.service.mapper.PayWayMapper;
import com.wxapp.pay.service.pay.PayFactory;
import com.wxapp.pay.service.service.PayService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author lgl
 */
@Service
@RequiredArgsConstructor
public class PayServiceImpl extends IBaseService implements PayService {
    private final PayWayMapper payWayMapper;
    private final PayFactory payFactory;
    private final PayConfigMapper payConfigMapper;
    private final SaleFeign saleFeign;
    private final PayLogsMapper payLogsMapper;
    private final RedisUtil redisUtil;
    private final ApplicationEventPublisher publisher;

    @Override
    public ResultVm<String> add(QPayAddVo qPayAddVo, UserVo userVo) {
        var payWay = CopyUtil.copyObjectNew(qPayAddVo, PayWayDO::new, (s, r) -> {
            r.setUserType(s.getUserType().stream().map(st -> st.getValue()).collect(Collectors.joining(",")));
            r.setCreateBy(userVo.getId());
            return null;
        });
        payWayMapper.insert(payWay);
        return success("添加成功");
    }

    @Override
    public ResultVm<String> update(QPayUpdateVo qPayUpdateVo, UserVo userVo) {
        var payWay = payWayMapper.selectById(qPayUpdateVo.getId());
        if (CheckUtil.isNullObject(payWay)) {
            return error("没有找到信息");
        }
        CopyUtil.copyObject(qPayUpdateVo, payWay, (s, r) -> {
            if (CheckUtil.isNotNullList(s.getUserType())) {
                r.setUserType(s.getUserType().stream().map(st -> st.getValue()).collect(Collectors.joining(",")));
            }
            r.setUpdateBy(userVo.getId());
            return null;
        });
        payWayMapper.updateById(payWay);
        return success("更新成功");
    }

    @Override
    public ResultVm<Page<PPayPageVm>> page(QPayPageVo vm) {
        Map<SFunction<PayWayDO, ?>, SqlKeyword> condition = new HashMap<>();
        condition.put(PayWayDO::getName, SqlKeyword.LIKE);
        var page = ConditionUtil.getPage(vm, PayWayDO.class);
        var conditions = ConditionUtil.getSCondition(PayWayDO::new, vm, PPayPageVm.class, condition, null);
        var pages = payWayMapper.selectPage(page, conditions);
        var result = CopyUtil.copyPage(pages, PPayPageVm::new, (s, r) -> {
            if (CheckUtil.isNotNullString(s.getUserType())){
                r.setUserType(ClientTypeEnum.findEnumByVales(s.getUserType().split(",")));
            }
            return null;
        });
        return success(result);
    }

    @Override
    public ResultVm<String> delGoods(String id) {
        var payWay = payWayMapper.selectById(id);
        if (CheckUtil.isNullObject(payWay)) {
            return error("没有找到信息");
        }
        payWay.setStatus(false);
        payWayMapper.updateById(payWay);
        return success("删除成功");
    }

    @Override
    public ResultVm<PPayDetailVm> detail(String id) {
        var payWay = payWayMapper.selectById(id);
        if (CheckUtil.isNullObject(payWay)) {
            return error("没有找到信息");
        }
        var detailVm = CopyUtil.copyObjectNew(payWay, PPayDetailVm::new, (s, r) -> {
            if (CheckUtil.isNotNullString(s.getUserType())){
                r.setUserType(ClientTypeEnum.findEnumByVales(s.getUserType().split(",")));
            }
            return null;
        });
        return success(detailVm);
    }

    @Override
    public  ResultVm<List<PPaySelectVm>> getPaySelect(){
        var query=new LambdaQueryWrapper<PayWayDO>();
        query.eq(PayWayDO::getStatus,true)
                .eq(PayWayDO::getStop,false);
        var payWay = payWayMapper.selectList(query);
        if (CheckUtil.isNullList(payWay)) {
            return success(new ArrayList<>());
        }
        return success(CopyUtil.copyListNew(payWay,PPaySelectVm::new,null));
    }
    @Override
    public ResultVm<String> pay(UserVo userVo, QPayVo vo) throws ThrowMyException {
        var orderInfo = saleFeign.getOrderInfoByCode(new QOrderInfoByCodeVo(vo.getOrderCode(), "UNPAID"));
        if (CheckUtil.isNullObject(orderInfo) || orderInfo.getCode() != 200 || CheckUtil.isNullObject(orderInfo.getData()) || CheckUtil.isNullString(orderInfo.getData().getCode())) {
            return error("没有找到订单信息");
        }
        var weChatPay = payFactory.choosePayWay(vo.getPayType());
        var orderInfoVo = CopyUtil.copyObjectNew(vo, QOrderInfoVo::new, (s, r) -> {
            r.setQOrderVm(orderInfo.getData());
            r.setUserType(userVo.getUserType());
            return null;
        });
        return success(weChatPay.executePay(orderInfoVo));
    }

    @Override
    public ResultVm<PPayStatusVm> getStatus(String orderCode) {
        PayLogsDO payLogsDO = (PayLogsDO) redisUtil.get(orderCode);
        if (CheckUtil.isNullObject(payLogsDO)) {
            return success(new PPayStatusVm());
        }
        return success(CopyUtil.copyObjectNew(payLogsDO, PPayStatusVm::new, null));
    }

    @Override
    @GlobalTransactional(rollbackFor = {Exception.class, RuntimeException.class})
    public ResultVm<List<PConfigVm>> wechatCallBack(QWechatCallBackVo vo) throws ThrowMyException {
        setStatus("", vo.getOrderCode(), "", "", "");
        return success(new ArrayList<>());
    }

    @Override
    @GlobalTransactional(rollbackFor = {Exception.class, RuntimeException.class})
    public ResultVm<String> aliPayCallBack(QAliPayCallBackVo vo) throws ThrowMyException {
        if (vo.getStatus() != PayStatusEnum.SUCCESS) {
            return success("error");
        }
        setStatus(vo.getApp_id(), vo.getOut_trade_no(), vo.getBuyer_logon_id(),vo.getBuyer_id(), vo.getTrade_no());
        return success("success");
    }

    /**
     * 支付成功回调
     * @param orderCode
     */
    private void setStatus(String appId, String orderCode, String account, String accountId, String payCode) throws ThrowMyException {
        var query = new LambdaQueryWrapper<PayLogsDO>();
        query.eq(PayLogsDO::getOrderCode, orderCode);
        var payLogsDO = payLogsMapper.selectFirst(query);
        if (CheckUtil.isNullObject(payLogsDO)) {
            throw new ThrowMyException("没有找到订单信息");
        }
        var payConfigQuery = new LambdaQueryWrapper<PayConfigDO>();
        payConfigQuery.eq(PayConfigDO::getStatus, true).eq(PayConfigDO::getConfigValue, appId);
        var payConfig = payConfigMapper.selectFirst(payConfigQuery);
        if (CheckUtil.isNullObject(payConfig)) {
            throw new ThrowMyException("没有找到支付方式");
        }
        var payWay = payWayMapper.selectById(payConfig.getPayWayId());
        if (CheckUtil.isNullObject(payWay)) {
            throw new ThrowMyException("没有找到支付方式");
        }
        payLogsDO.setPayStatus(PayStatusEnum.SUCCESS);
        payLogsDO.setCompleteTime(LocalDateTime.now());
        payLogsDO.setAccount(account);
        payLogsDO.setAccountId(accountId);
        payLogsDO.setPayCode(payCode);
        payLogsMapper.updateById(payLogsDO);
        redisUtil.set(CatchCommonConstant.PAY_STATUS + orderCode, payLogsDO, 7200);
        publisher.publishEvent(new PayCallBackEvent(this, "支付成功调用", new QPayCallBackVo(orderCode, payWay.getCode(), LocalDateTime.now(), account)));
    }
}
